#include <DS1307RTC.h>
#include <Time.h>
#include <Wire.h>
#include <SD.h>
#include <EEPROMEx.h>
#include <SPI.h>
#include <Ethernet.h>

File archivo;
tmElements_t tm;

#define totalAnalogicos 16
#define totalDigitales 16

byte analogicos[totalAnalogicos];
byte tipoSensores[totalAnalogicos];
byte sensores[totalAnalogicos];

byte digitales[totalDigitales];

int numeroEntradas=0;
char character;
String comando="";

String archivoLogger="logger.txt";
String archivoConfiguracion="config.txt";
int muestreo = 3000;
boolean guardandoSD=false;
String fecha="21/01/2014 00\\:00\\:00";
long ultimoEnvio = 0;
String separado[6];
boolean initEeprom=false;
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
IPAddress localIP(192,168,0,20);
EthernetClient client;
EthernetServer server(888);

void setup() {
  Serial.begin(9600);
  Wire.begin();
  pinMode(53,OUTPUT);
  pinMode(10, OUTPUT);
  digitalWrite(10,HIGH);
  if (!SD.begin(4)){
    Serial.println("No se pudo inicializar la SD");
  }
  Ethernet.begin(mac,localIP);
  server.begin();
  Serial.print("server is at ");
  Serial.println(Ethernet.localIP());
  delay(1000);
  if(initEeprom)
    inicializarEeprom();
  cargarConfiguracion();
  configurarParametros();
}

void loop() {
  verificarComando();
  if(guardandoSD)
    if( ( millis() - ultimoEnvio ) > muestreo ){
      enviarValores(false);
    }
}

void enviarValores(boolean enviar){
  String valores[numeroEntradas];
  int contador=0;
  String V="V:";
  for(int i=0;i<totalAnalogicos;i++){
   if(analogicos[i]==1){
     String num =String(i,DEC);
     V+="A"+num;
     String a = String (analogRead(i),DEC);
     V+=":"+a+"-";
     valores[contador]=a;
     contador++;
   }
  }
  for(int i=0;i<totalDigitales;i++){
   if(digitales[i]==1){
     String num =String(i,DEC);
     V+="D"+num;
     String d = String (digitalRead(i),DEC);
     V+=":"+d+"-";
     valores[contador]=d;
     contador++;
   }
  }
  V=V.substring(0,V.length()-1);
  //Serial.println(V);
  if(enviar){
    enviarMensaje(V);
  } else {
    guardarEnSD(valores,numeroEntradas); 
    ultimoEnvio =millis();
  }
}
//0=Enviar Valores | Solo recibe un 0
//1=El micro guarda en SD la configuracion. | Solo recibe un 1
//2=El micro envia lo que hay en la SD. | Solo recibe un 2
//3=Procesa los pines digitales. | 32:1 (El 3 es el id del comando el 3 significa que es el pin 3 y el 1 que se va a prender)
//4=Procesa los pines PWM. | 45:79 (El 4 es el id del comando el 5 significa que es el pin 5 y el 79 el valor del PWM)
//5=Lee lo que hay de salida. | 5:D2:D3 (El 5 es el id del comando y los demas son los id de los pines)
//6=Actualiza los paramestro muestreo, fecha y hora y si guarda o no en SD. | 6:3000-0-30/01/2014 22:23:04-file5.csv
//7=Lee los parametros muestreo, fecha y hora y si guarda o no en SD que tiene guardado el arduino. | Solo recibe un 7
//8=Guarda en un archivo la propiedad de la grafica. | 8:Ventana nueva test:Volts:SECUENCIAL:0::A0-A2-A3:Ventana Nueva:clip_image0041.jpg:clip_image0041.txt
void verificarComando(){
  client = server.available();
  if (client) {
    while(client.available()) {
      Serial.print(".");
      character = client.read();
      comando.concat(character);
    }
    if (character == 10){
      comando.trim();
      Serial.print("Comando: ");
      Serial.println(comando);
      switch(comando.substring(0,1).toInt()){
        case 0:enviarValores(true);break;
        case 1:guardarConfiguracion();break;
        case 2:enviarConfiguracion();break;
        case 3:procesarDigital(comando.substring(1));break;
        case 4:procesarPWM(comando.substring(1));break;
        case 5:leerSalidas(comando.substring(2));break;
        case 6:actualizarParametros(comando.substring(2));break;
        case 7:leerParametros();break;
        case 8:guardarEditarGrafica(comando.substring(2));break;
      }
      comando="";
      character='x';
      client.flush();
      client.stop();
    } 
  }
}

void guardarEnSD(String valores[], int length){
  String line = "";
  line+=String(getDate());
  line+=",";
  line+=String(getHora());
  for(int i=0;i<length; i++){
    line += ",";
    line += valores[i];
  }
  char filename[archivoLogger.length()+1];
  archivoLogger.toCharArray(filename,archivoLogger.length()+1);
  Serial.println(archivoLogger);
  File dataFile = SD.open(filename, FILE_WRITE);
  if (dataFile) {
    Serial.println("Guardando en SD");
    dataFile.println(line);
    dataFile.close();
  } 
  //Serial.println(line);
}

void leerSalidas(String comando){
  int total = countCharacter(comando,':')+1;
  split(comando,':');
  for(int i=0; i<total; i++){
    //*Serial.println(separado[i]);
    leerDigital(separado[i].substring(1).toInt());
  }
}

void leerDigital(int pin){
  int valor = digitalRead(pin);
  String salida = "S:D";
  salida = salida+pin+":" + valor;
  enviarMensaje(salida);
}

void procesarDigital(String comando){
  split(comando,':');
  digitalWrite(separado[0].toInt(),separado[1].toInt());
}

void procesarPWM(String comando){
  split(comando,':');
  analogWrite(separado[0].toInt(),separado[1].toInt());
}

void actualizarParametros(String comando){
  Serial.println("Actualizando parametros..");
  Serial.println(comando);
  split(comando,'-');
  Serial.println(separado[0]);
  muestreo = separado[0].toInt();
  EEPROM.writeInt(0,muestreo);delay(50);
  if(separado[1].toInt()==1)
    guardandoSD=true;
  else
    guardandoSD=false;
  EEPROM.writeInt(2,separado[1].toInt());delay(50);
  fecha = String(separado[2]); 
  archivoLogger = separado[3];
  EEPROM.writeInt(4,archivoLogger.length());delay(50);
  char loggerArray[archivoLogger.length()];
  if(!SD.exists(loggerArray)){
    crearArchivo(archivoLogger);
  }
  archivoLogger.toCharArray( loggerArray , archivoLogger.length());
  EEPROM.writeBlock<char>(6,loggerArray,archivoLogger.length());
  delay(10);
  configurarFecha();
}

void leerParametros(){
  String f = getDate();
  String parametros = "P:";
  parametros += String(muestreo);
  parametros += String("-");
  parametros += String(guardandoSD);
  parametros += String("-");
  parametros += String(f);
  parametros += String("-");
  parametros += archivoLogger;
  Serial.println("Parametros");
  Serial.print(parametros);
  enviarMensaje(parametros);
}

void guardarConfiguracion(){
  String linea="";
  char filename[archivoConfiguracion.length()+1];
  archivoConfiguracion.toCharArray(filename,archivoConfiguracion.length()+1);
  SD.remove(filename);
  crearArchivo(filename);
  delay(10);
  archivo = SD.open(filename, FILE_WRITE);
  if(archivo){
    Serial.println("Guardando Configuracion..");
    while(!linea.startsWith("EOF")){
      linea="";
      client = server.available();
      if (client) {
        while(client.available()) {
          character = client.read();
          linea.concat(character);
        }
        if (character == 10){
          linea.trim();
          Serial.print(linea+"\n");
          archivo.print(linea+"\n");
          character='x';
          client.flush();
          client.stop();
        }
      }
    }
    Serial.println("Termina de guardar");
    archivo.close();
    cargarConfiguracion();
  }
  else
    Serial.println("Error al abrir el archivo");
}

void enviarConfiguracion(){
  char filename[archivoConfiguracion.length()+1];
  archivoConfiguracion.toCharArray(filename,archivoConfiguracion.length()+1);
  archivo = SD.open(filename);
  if (archivo) {
    String linea="";
    enviarMensaje("CONFIGURACIONSD");
    while (archivo.available()) {
      char caracter = archivo.read();
      linea.concat(caracter);
      if(caracter ==10){
        enviarMensaje(linea);
        linea="";
        delay(50);
      }
    }
    archivo.close();
    File gf = SD.open("grafica.txt");
    if(gf){
      String linea="";
      while (gf.available()) {
        char caracter = gf.read();
        linea.concat(caracter);
        if(caracter ==10){
          Serial.print(linea);
          enviarMensaje(linea);
          linea="";
          delay(20);
        }
      }
    }
    enviarMensaje("EOFSD");
  } else {
    Serial.print("Error al abrir ");
    Serial.println(archivoConfiguracion);
  }
  leerParametros();
}

void split(String line, char character){
  const char * s = &character;
  line+=" ";
  char sz[line.length()];
  line.toCharArray(sz,line.length());
  char *p = sz;
  char *str;
  int count=0;
  while ((str = strtok_r(p, s, &p)) != NULL){
    String c =str;
    //Serial.println(str);
    separado[count] = c;
    count++;
  }
}

int countCharacter(String line,char character){
  int counter = 0;
  for( int i=0; i<line.length(); i++ ) {
    if( line.charAt(i) == character ) {
      counter++;
    } 
  }
  return counter;
}

String getDate(){
  tmElements_t tm;
  String date = "";
  RTC.read(tm);
  date += print2digits(tm.Day);
  date += String('/');
  date += print2digits(tm.Month);
  date += String('/');
  date += tmYearToCalendar(tm.Year);
  date += String(' ');
  date += print2digits(tm.Hour);
  date += String(':');
  date += print2digits(tm.Minute);
  date += String(':');
  date += print2digits(tm.Second);
  return date;
}

String getHora(){
  tmElements_t tm;
  String date = "";
  RTC.read(tm);
  date += print2digits(tm.Hour);
  date += String(':');
  date += print2digits(tm.Minute);
  date += String(':');
  date += print2digits(tm.Second);
  return date;
}

String print2digits(int number) {
  String new_number;
  if (number >= 0 && number < 10) {
    new_number ="0";
  }
  new_number += String(number,DEC);
  return new_number;
}

void cargarConfiguracion(){
  numeroEntradas=0;
  for(int i=0;i<totalAnalogicos;i++){
    String numero = String(i,DEC);
     String id ="A"+numero;
     String valor = getPropiedad(id);
     if(valor != NULL){
       if(valor.equals("true")){
         analogicos[i]=1;
         numeroEntradas++;
       }
       else{
         analogicos[i]=0;
       }
     }
     String ts ="TIPOSENSOR"+id;
     tipoSensores[i]=getPropiedad(ts).toInt();
     String s = "SENSOR"+id;
     sensores[i]=getPropiedad(s).toInt();
  }
  for(int i=0;i<totalDigitales;i++){
    String numero = String(i,DEC);
    String id ="D"+numero;
    String valor = getPropiedad(id);
    if(valor != NULL)
      if(valor.equals("true")){
        digitales[i]=1;
        numeroEntradas++;
        pinMode(i, INPUT); 
      }
      else{
        digitales[i]=0;
        pinMode(i, OUTPUT); 
      }
  }
}

String getPropiedad(String propiedad){
  String valor = NULL;
  archivo = SD.open("config.txt");
  String linea = "";
  if (archivo) {
    while (archivo.available()) {
      char caracter = archivo.read();
      linea.concat(caracter);
      if (caracter == 10){
        linea.trim();
        if(!linea.equals("")){
          String nombrePropiedad = getNombrePropiedad(linea);
          if(nombrePropiedad!=NULL){
            if(nombrePropiedad.equals(propiedad)){
              valor = getValorPropiedad(linea);
              goto outerloop;
            }
          }
        }
        linea="";
        caracter='x';
      }
    }
    outerloop:
    archivo.close();
  } else {
    Serial.println("Error al abrir el archivo");
  }
  return valor;
}

String getNombrePropiedad(String linea){
  int i = linea.indexOf('=');
  if(i==-1)
    return NULL;
  String sub = linea.substring(0,i);
  return sub;
}

String getValorPropiedad(String linea){
  int i = linea.indexOf('=');
  if(i==-1)
    return NULL;
  String sub = linea.substring(i+1,linea.length());
  return sub;
}

void guardarEditarGrafica(String valor){
  if(SD.exists("grafica.txt"))
    SD.remove("grafica.txt");
  crearArchivo("grafica.txt");
  archivo = SD.open("grafica.txt", FILE_WRITE);
  if(archivo){
    archivo.print("graficas=");
    archivo.println(valor);
    archivo.close();
  } else{
   Serial.println("Error al abrir el archivo graficaConfig.txt"); 
  }
}

void crearArchivo(String nombre){
  char filename[nombre.length()+1];
  archivoConfiguracion.toCharArray(filename,nombre.length()+1);
  File f = SD.open(filename, FILE_WRITE);
  f.close();
  delay(5);
}

void configurarFecha(){
  fecha.replace("\\","");
  fecha.replace(" ",":");
  fecha.replace("/",":");
  split(fecha,':');
  setTime(separado[3].toInt(),separado[4].toInt(),separado[5].toInt(),separado[0].toInt(),separado[1].toInt(),separado[2].toInt());
  RTC.set(now());
}

void configurarParametros(){
  int longitud = EEPROM.readInt(4);
  char output[longitud];
  EEPROM.readBlock<char>(6, output, longitud);
  archivoLogger = String(output);
  if(!SD.exists(output)){
    crearArchivo(archivoLogger);
  }
  muestreo = EEPROM.readInt(0);
  int sd = EEPROM.readInt(2);
  if(sd == 1)
    guardandoSD=true;
  else
    guardandoSD=false;
}

void inicializarEeprom(){
  split(comando,'-');
  Serial.println("Inicializando eeprom..");
  EEPROM.writeInt(0,muestreo);delay(50);
  EEPROM.writeInt(2,0);delay(50);
  EEPROM.writeInt(4,archivoLogger.length());delay(50);
  char loggerArray[archivoLogger.length()];
  archivoLogger.toCharArray( loggerArray , archivoLogger.length());
  EEPROM.writeBlock<char>(6,loggerArray,archivoLogger.length());
  delay(10);
  configurarFecha();
}

void enviarMensaje(String msg){
  client.println(msg);
}
